package com.yenroc.invoice.util;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class DateUtils {
    /**
     * 日期格式, 年份
     */
    public static final String DATE_FORMAT_YYYY = "yyyy";
    /**
     * 日期格式, 月份
     */
    public static final String DATE_FORMAT_MM = "MM";
    /**
     * 日期格式, 年份和月份
     */
    public static final String DATE_FORMAT_YYYYMM = "yyyyMM";
    /**
     * 日期格式, 年份和月份
     */
    public static final String DATE_FORMAT_YYYY_MM = "yyyy-MM";
    /**
     * 日期格式, 年月日
     */
    public static final String DATE_FORMAT_YYMMDD = "yyMMdd";
    /**
     * 日期格式, 年月日, 用横杠分开
     */
    public static final String DATE_FORMAT_YY_MM_DD = "yy-MM-dd";
    /**
     *
     */
    public static final String DATE_FORMAT_YYYYMMDD = "yyyyMMdd";
    /**
     * 日期格式, 年月日, 用横杠分开
     */
    public static final String DATE_FORMAT_YYYY_MM_DD = "yyyy-MM-dd";
    /**
     * 日期格式, 年月日, 用横杠分开
     */
    public static final String DATE_FORMAT_YYYY_MM_DD_WITH_ONE_MONTH = "yyyy-M-d";
    /**
     * 日期格式, 年月日
     */
    public static final String DATE_FORMAT_POINTYYYYMMDD = "yyyy.MM.dd";
    /**
     * 日期格式, 年月日
     */
    public static final String DATE_TIME_FORMAT_YYYY年MM月DD日 = "yyyy年MM月dd日";
    /**
     * 日期格式, 年月日时分
     */
    public static final String DATE_FORMAT_YYYYMMDDHHmm = "yyyyMMddHHmm";
    /**
     * 日期格式, 年月日 时分
     */
    public static final String DATE_TIME_FORMAT_YYYYMMDD_HH_MI = "yyyyMMdd HH:mm";

    /**
     * 日期格式, 年月日时分
     */
    public static final String DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI = "yyyy-MM-dd HH:mm";
    /**
     * 日期格式, 年月日时分秒
     */
    public static final String DATE_TIME_FORMAT_YYYYMMDDHHMISS = "yyyyMMddHHmmss";
    /**
     * 日期格式, 年月日时分秒, 年月日用横杠分开, 时分秒用冒号分开
     */
    public static final String DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS = "yyyy-MM-dd HH:mm:ss";
    /**
     * 一位月份的日期格式, 年月日时分秒, 年月日用横杠分开, 时分秒用冒号分开
     */
    public static final String DATE_TIME_FORMAT_YYYY_M_D_H_M_S_WITH_ONE = "yyyy-M-d H:m:s";
    /**
     * 两位时分秒, 用冒号分开
     */
    public static final String DATE_TIME_FORMAT_HH_MI_SS = "HH:mm:ss";
    /**
     * 日期格式, 年月日时分秒毫秒
     */
    public static final String DATE_TIME_FORMAT_YYYYMMDDHHMISSSSS = "yyyyMMddHHmmssSSS";
    /**
     * 日期格式, 月日时分
     */
    public static final String DATE_FORMAT_MMDDHHMI = "MM-dd HH:mm";

    private static final Long msOfDay = 24 * 60 * 60 * 1000L;

    private static final Long msOfHour = 60 * 60 * 1000L;

    private static final Long msOfMinute = 60 * 1000L;

    public static final Long TIMEZONE_OFFSET_MILLISECOND = ZoneId.systemDefault().getRules().getOffset(Instant.now()).getTotalSeconds() * 1000L;

    /* ************工具方法***************   */

    /**
     * 获取当前时间n天后的0点时间戳
     *
     * @param millisecond 毫秒值
     * @param plusDays    后延天数(如传负数则表示提前指定天数)
     * @return long型毫秒值
     */
    public static Long getDayStartMilliTime(Long millisecond, long plusDays) {
        if (millisecond == null || millisecond <= 0) {
            return null;
        }
        return LocalDateTime.ofEpochSecond(millisecond / 1000, 0, ZoneOffset.ofHours(8)).plusDays(plusDays).withHour(0).withMinute(0).withSecond(0).toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
    }

    public static Long atStartOfDayByLong(Long timestamp) {
        if (timestamp == null || timestamp <= 0) {
            return null;
        }
        return Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault()).toLocalDate().atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    public static Long atStartOfDayByString(String dateTimeStr, String timeFormat) {
        if (dateTimeStr == null || dateTimeStr.length() <= 0) {
            return null;
        }
        return LocalDate.parse(dateTimeStr, DateTimeFormatter.ofPattern(timeFormat)).atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 获取指定月份的日期列表(如果是今年当前月,则返回1号到昨天的日期列表)
     *
     * @param dateFrom 指定日期字符串(如20210101)
     * @param formatBy 上放参数的format格式(如yyyyMMdd)
     * @return 日期列表(dd)
     */
    public static List<String> getDaysListByMonth(String dateFrom, String formatBy) {
        LocalDate now = LocalDate.now();
        LocalDate date;
        if (dateFrom == null) date = now.withDayOfMonth(1);
        else date = LocalDate.parse(dateFrom, DateTimeFormatter.ofPattern(formatBy));
        int month = date.getMonthValue();
        List<String> list = new ArrayList();
        DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd");
        if (date.getYear() == now.getYear() && month == now.getMonthValue()) {
            while (date.getMonthValue() == month && date.getDayOfMonth() < now.getDayOfMonth()) {
                list.add(dateFormat.format(date));
                date = date.plusDays(1);
            }
        } else {
            while (date.getMonthValue() == month) {
                list.add(dateFormat.format(date));
                date = date.plusDays(1);
            }
        }
        return list;
    }

    /**
     * 获取当前时间的 日期字符串
     * @param formatter 日期格式
     * @return String 自动格式的字符串
     */
    public static String getDateString(String formatter) {
        return getDateString(LocalDateTime.now(), formatter);
    }

    /**
     * 获取指定时间的 日期字符串
     * @param formatter 日期格式
     * @return String 自动格式的字符串
     */
    public static String getDateString(Long millisecond, String formatter) {
        LocalDateTime localDateTime;
        if (millisecond != null) {
            localDateTime = Instant.ofEpochMilli(millisecond).atZone(ZoneId.systemDefault()).toLocalDateTime();
        } else localDateTime = LocalDateTime.now();
        return getDateString(localDateTime, formatter);
    }

    /**
     * 获取指定时间的字符串
     * @param localDateTime LocalDateTime
     * @param formatter 日期格式
     * @return String
     */
    public static String getDateString(LocalDateTime localDateTime, String formatter) {
        return localDateTime.format(DateTimeFormatter.ofPattern(formatter));
    }

    /**
     * 获取指定格式的日期字符串
     *
     * @param dateStr   日期字符串
     * @param beforeFormatter 当前日期格式
     * @param afterFormatter 转换后日期格式
     * @return 日期字符串
     */
    public static String getDateString(String dateStr, String beforeFormatter, String afterFormatter) {
        if (dateStr == null || dateStr.length() <= 0) {
            return null;
        }
        long millisecond = LocalDate.from(LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(beforeFormatter)))
                .atStartOfDay(ZoneId.systemDefault())
                .toInstant()
                .toEpochMilli();
        return getDateString(millisecond, afterFormatter);
    }

    public static String formatDateWithTimeMillions(Long timestamp, String formatBy) {
        return Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern(formatBy));
    }

    public static String formatDateWithTimeMillions(Long timestamp, String formatBy, int plusDay) {
        return Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault()).plusDays(plusDay).format(DateTimeFormatter.ofPattern(formatBy));
    }

    /**
     * 获取指定月份第一天时间戳, null值默认当月
     * @param millisecond 时间戳
     * @return 时间戳
     */
    public static long getMonthFirstDayTimestamp(Long millisecond) {
        // 获取当前日期
        LocalDate now;
        if (millisecond != null) {
            now = Instant.ofEpochMilli(millisecond).atZone(ZoneId.systemDefault()).toLocalDate();
        } else now = LocalDate.now();
        // 设置日期为该月第一天
        LocalDate firstDayOfMonth = now.withDayOfMonth(1);
        // 获取该月第一天的时间戳
        return firstDayOfMonth.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 获取指定月份最后一天时间戳, null值默认当月
     * @param millisecond 时间戳
     * @return 时间戳
     */
    public static long getMonthLastDayTimestamp(Long millisecond) {
        LocalDate now;
        if (millisecond != null) {
            now = Instant.ofEpochMilli(millisecond).atZone(ZoneId.systemDefault()).toLocalDate();
        } else now = LocalDate.now();
        LocalDate lastDayOfMonth = now.withDayOfMonth(now.lengthOfMonth());
        return lastDayOfMonth.atTime(LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 获取两个时间戳间隔天数
     * @param millisecond1 毫秒值1
     * @param millisecond2 毫秒值2
     * @return 天数
     */
    public static long intervalDays(Long millisecond1, Long millisecond2) {
        if (Objects.equals(millisecond1, millisecond2) || millisecond1 == null || millisecond2 == null) return 0;
        LocalDateTime dateTime1 = LocalDateTime.ofInstant(
                Instant.ofEpochMilli(millisecond1), ZoneId.systemDefault());
        LocalDateTime dateTime2 = LocalDateTime.ofInstant(
                Instant.ofEpochMilli(millisecond2), ZoneId.systemDefault());
        return Math.abs(ChronoUnit.DAYS.between(dateTime1.toLocalDate(), dateTime2.toLocalDate()));
    }

    /**
     * 获取两个时间戳间隔日期的毫秒值
     *
     * @param millisecond1 毫秒值1
     * @param millisecond2 毫秒值2
     * @return 间隔日期的毫秒值
     */
    public static List<Long> listIntervalDay(Long millisecond1, Long millisecond2) {
        long daysBetween = intervalDays(millisecond1, millisecond2);
        List<Long> dayList = new ArrayList<>();
        for (int i = 0; i <= daysBetween; i++) {
            dayList.add(getDayStartMilliTime(millisecond1, i));
        }
        return dayList;
    }

    /**
     * 获取两个时间戳间隔的日期字符串
     *
     * @param millisecond1 毫秒值1
     * @param millisecond2 毫秒值2
     * @param formatter    格式化
     * @return 间隔日期的字符串
     */
    public static List<String> listIntervalDateString(Long millisecond1, Long millisecond2, String formatter) {
        List<Long> dayList = listIntervalDay(millisecond1, millisecond2);
        if (formatter == null || formatter.isEmpty()) {
            formatter = DATE_FORMAT_YYYY_MM_DD;
        }
        String pattern = formatter;
        return dayList.stream()
                .map(d -> DateUtils.getDateString(d, pattern))
                .collect(Collectors.toList());
    }

}